LinkLuaModifier("modifier_chest", "modifiers/modifier_chest", LUA_MODIFIER_MOTION_NONE)

CHEST_OPERATION_RADIUS = 100

Chest = Chest or class({}, nil, UnitEntity)

function Chest:constructor(row, colm, unitName, location, findClearSpace, owner, teamNumber)
	self.row = row or 1
	self.colm = colm or 1
	self.locked = false

	getbase(Chest).constructor(self, unitName, location, findClearSpace, owner, teamNumber)
end

function Chest:SetUnit(unit)
	getbase(Chest).SetUnit(self, unit)

	self:SetChest(self.row, self.colm)
	self:AddNewModifier(self, nil, "modifier_chest", nil)

	self:GetUnit():SetControllableByPlayer(-1, true)
	self:ResolveNPCPositions()
end

function Chest:SetChest(row, colm)
	if self.chest == nil then
		self.chest = ItemContainer(self, row, colm)
		self:SetNetTable("chest", self.chest:GetIndex())
		self:UpdateNetTable()
	else
		self.chest:SetRow(row)
		self.chest:SetColm(colm)
	end
end

function Chest:GetChest()
	return self.chest
end

function Chest:AddItem(item)
	return self.chest:AddItem(item)
end

function Chest:Lock()
	self.locked = true
end

function Chest:Unlock()
	self.locked = false
end

function Chest:ResolveNPCPositions()
	local units = FindUnitsInRadius(DOTA_TEAM_GOODGUYS, self:GetPos(), nil, self:GetUnit():GetHullRadius()+CHEST_OPERATION_RADIUS, DOTA_UNIT_TARGET_TEAM_BOTH, DOTA_UNIT_TARGET_BASIC+DOTA_UNIT_TARGET_HERO, DOTA_UNIT_TARGET_FLAG_INVULNERABLE+DOTA_UNIT_TARGET_FLAG_MAGIC_IMMUNE_ENEMIES, FIND_CLOSEST, false)
	for _,unit in pairs(units) do
		if unit ~= self:GetUnit() and (unit.GetParentEntity ~= nil and not unit:GetParentEntity():IsChest()) then
			FindClearSpaceForUnit(unit, unit:GetAbsOrigin(), false)
		end
	end
end

function Chest:Open(operatingUnit)
	if operatingUnit == nil then
		return
	end

	if self.locked then
		return
	end

	if self:IsOpen() then
		return
	end

	self:GetUnit():SetControllableByPlayer(operatingUnit:GetUnit():GetPlayerOwnerID(), true)
	self:ResolveNPCPositions()

	self.operatingUnit = operatingUnit

	local openChests = operatingUnit:GetNetTable("openChests") or {}
	table.insert(openChests, self.chest:GetIndex())
	operatingUnit:SetNetTable("openChests", openChests)

	self.operatingUnit:GetUnit():SetContextThink(DoUniqueString("OpenChest"), 
		function()
			if not self:IsOpen() then
				return
			end

			if not self.operatingUnit:IsAlive() then
				self:Close()
				return
			end

			if self:IsRemove() then
				self:Close()
				return
			end

			if not self.operatingUnit:GetUnit():IsPositionInRange(self:GetPos(), self.operatingUnit:GetUnit():GetHullRadius()+CHEST_OPERATION_RADIUS+self:GetUnit():GetHullRadius()) then
				self:Close()
				return
			end
			return 0
		end
	, 0)
end

function Chest:Close()
	if not self:IsOpen() then
		return
	end

	self:GetUnit():SetControllableByPlayer(-1, true)
	self:ResolveNPCPositions()

	local openChests = self.operatingUnit:GetNetTable("openChests") or {}
	for i = #openChests, 1, -1 do
		if openChests[i] == self.chest:GetIndex() then
			table.remove(openChests, i)
			break
		end
	end
	self.operatingUnit:SetNetTable("openChests", openChests)
	self.operatingUnit = nil
end

function Chest:MoveToOpen(unit)
	if unit:GetUnit():IsPositionInRange(self:GetPos(), self:GetUnit():GetHullRadius()+CHEST_OPERATION_RADIUS+unit:GetUnit():GetHullRadius()) then
		if self:GetOperatingUnit() == unit then
			unit:GetUnit():Interrupt()
			self:Close()
		else
			unit:GetUnit():Interrupt()
			self:Open(unit)
		end
	else
		ExecuteOrderFromTable(
			{
				UnitIndex = unit:GetUnit():entindex(),
				OrderType = DOTA_UNIT_ORDER_MOVE_TO_TARGET,
				TargetIndex  = self:GetUnit():entindex(),
			}
		)
		unit:GetUnit():SetContextThink("MoveToOpenChest", 
			function()
				if not unit:IsAlive() then
					return
				end

				if unit:GetUnit().orderTarget ~= self:GetUnit() then
					return
				end

				if self:IsRemove() then
					return
				end

				if unit:GetUnit():IsPositionInRange(self:GetPos(), self:GetUnit():GetHullRadius()+CHEST_OPERATION_RADIUS+unit:GetUnit():GetHullRadius()) then
					unit:GetUnit():Interrupt()
					self:Open(unit)
					return
				end
				return 0
			end
		, 0)
	end
end

function Chest:IsOpen()
	return self.operatingUnit ~= nil
end

function Chest:GetOperatingUnit()
	return self.operatingUnit
end

getbase(Chest).IsChest = function(self)
	return false
end
function Chest:IsChest()
	return true
end

function Chest:Remove()
	self:Close()
	self.chest:Remove()

	getbase(Chest).Remove(self)
end